#include "tffmpegthread.h"
#include <QDebug>
#include <QTime>
#include <QImage>
#include <QFile>
#include <QElapsedTimer>
#include <QAudioFormat>
#include <QAudioDeviceInfo>
#include <QAudio>

/*
 * 打开回调函数
 */
static int AVInterruptCallBackFun(void *ctx)
{
    return 0;
}

TFFmpegThread::TFFmpegThread(QObject *parent) : QThread(parent)
{
    m_ffmpegVideoPlayThread = new TFFmpegVideoPlayThread;
    m_ffmpegAudioPlayThread = new TFFmpegAudioPlayThread;
    //信号槽
    {
        //视频帧信号
        connect(m_ffmpegVideoPlayThread,&TFFmpegVideoPlayThread::sigGetVideoFrame,this,&TFFmpegThread::sigGetVideoFrame);
    }
}

TFFmpegThread::~TFFmpegThread()
{

}

/*
 * 打印支持的库
 */
void TFFmpegThread::showLib()
{
    //输出所有支持的解码器名称
    QStringList listCodeName;
    void *opaque = nullptr;
    const AVCodec *codec = nullptr;

    while((codec = av_codec_iterate(&opaque))) {
        if(codec) {
            listCodeName.append(codec->name);
            continue;
        }
        else {
            break;
        }
    }
    qDebug() << "supported codec names" << listCodeName;

    //输出支持的协议
    QStringList listProtocolsIn, listProtocolsOut;
    struct URLProtocol *protocol = nullptr;
    struct URLProtocol **protocol2 = &protocol;

    avio_enum_protocols((void **)protocol2, 0);
    while ((*protocol2) != nullptr) {
        listProtocolsIn << avio_enum_protocols((void **)protocol2, 0);
    }
    qDebug() << "supported protocols in" << listProtocolsIn;
    protocol = nullptr;
    avio_enum_protocols((void **)protocol2, 1);
    while ((*protocol2) != nullptr) {
        listProtocolsOut << avio_enum_protocols((void **)protocol2, 1);
    }
    qDebug() << "supported protocols out" << listProtocolsOut;
}

/*
 * 启动
 * nurl : 地址
 */
void TFFmpegThread::playMedia(const QString &nurl)
{
    if(!isRunning()) {
        m_url = nurl;
        m_stopped = false;
        start();
    }
}

/*
 * 停止
 */
void TFFmpegThread::closeMedia()
{
    if(isRunning()) {
        m_ffmpegAudioPlayThread->stopPlay();
        m_ffmpegVideoPlayThread->stopPlay();
        m_stopped = true;
        quit();
        wait();
        m_ffcustomPlayer.audiopktList.clear();
        m_ffcustomPlayer.videopktList.clear();
    }
}

/*
 * 获取URL
 * 返回值 : URL
 */
QString TFFmpegThread::getUrl()
{
    return m_url;
}

/*
 * 设置URL
 */
void TFFmpegThread::setUrl(const QString &nurl)
{
    m_url = nurl;
}

/*
 * 初始化
 * 返回值 : true:成功 false:失败
 */
bool TFFmpegThread::init()
{
    //初始化参数
    initOption();
    //初始化输入
    if(!initInput()) {
        return false;
    }
    //初始化视频
    if(!initVideo()) {
        return false;
    }
    //初始化音频
    if(!initAudio()) {
        return false;
    }
    //初始化其它
    initOther();

    return true;
}

/*
 * 初始化参数
 */
void TFFmpegThread::initOption()
{
    //在打开码流前指定各种参数比如:探测时间/超时时间/最大延时等
    //设置缓存大小,1080p可将值调大,现在很多摄像机是2k可能需要调大,一般2k是1080p的四倍
    av_dict_set(&m_options, "buffer_size", "8192000", 0);
    //通信协议采用tcp还是udp,通过参数传入,不设置默认是udp
    //udp优点是无连接,在网线拔掉以后十几秒钟重新插上还能继续接收,缺点是网络不好的情况下会丢包花屏
    av_dict_set(&m_options, "rtsp_transport", m_transport.toUtf8().constData(), 0);
    //设置超时断开连接时间,单位微秒,3000000表示3秒
    av_dict_set(&m_options, "stimeout", "3000000", 0);
    //设置最大时延,单位微秒,1000000表示1秒
    av_dict_set(&m_options, "max_delay", "1000000", 0);
    //自动开启线程数
    av_dict_set(&m_options, "threads", "auto", 0);
}

/*
 * 初始化输入
 * 返回值 : true:成功 false:失败
 */
bool TFFmpegThread::initInput()
{
    //实例化格式处理上下文
    m_ffcustomPlayer.format_ctx = avformat_alloc_context();
    //设置超时回调,有些不存在的地址或者网络不好的情况下要卡很久
    //此函数用于防止阻塞 返回1就会停止
    m_ffcustomPlayer.format_ctx->interrupt_callback.callback = AVInterruptCallBackFun;
    m_ffcustomPlayer.format_ctx->interrupt_callback.opaque = this;

    AVInputFormat *ifmt = nullptr;
    //打开要处理的流
    int nret = avformat_open_input(&m_ffcustomPlayer.format_ctx,m_url.toUtf8().data(),ifmt,&m_options);
    if(nret < 0) {
        qDebug() << "open input error" << TFFmpegSupporter::getError(nret);
        return false;
    }
    //释放设置参数
    if(m_options) {
        av_dict_free(&m_options);
    }
    //获取流信息
    nret = avformat_find_stream_info(m_ffcustomPlayer.format_ctx,nullptr);
    if(nret < 0) {
        qDebug() << "find stream info error" << TFFmpegSupporter::getError(nret);
        return false;
    }
    return true;
}

/*
 * 初始化视频
 * 返回值 : true:成功 false:失败
 */
bool TFFmpegThread::initVideo()
{
    bool nret = m_ffmpegVideoPlayThread->initVideo();
    return nret;
}

/*
 * 初始化音频
 */
bool TFFmpegThread::initAudio()
{
    bool nret = m_ffmpegAudioPlayThread->initAudio();
    return nret;
}

/*
 * 初始化其它
 */
void TFFmpegThread::initOther()
{
    //分配解码包内存
    m_packet = av_packet_alloc();
    //解码格式
    m_formatName = m_ffcustomPlayer.format_ctx->iformat->name;
    m_duration = m_ffcustomPlayer.format_ctx->duration / AV_TIME_BASE;
    QString nfileinfostr = QString("文件信息 -> 格式: %1 时长: %2 秒").arg(m_formatName).arg(m_duration);
    qDebug() << nfileinfostr;
    emit sigLog(nfileinfostr);
}

/*
 * 释放对象
 */
void TFFmpegThread::freeMember()
{
    if(m_packet) {
        av_packet_unref(m_packet);
        m_packet = nullptr;
    }
    if(m_ffcustomPlayer.format_ctx) {
        avformat_close_input(&m_ffcustomPlayer.format_ctx);
        m_ffcustomPlayer.format_ctx = nullptr;
    }
    av_dict_free(&m_options);

}

/*
 * 释放对象(帧)
 * nframe : 帧
 */
void TFFmpegThread::freeMember(AVFrame *nframe)
{
    if(nframe) {
        av_frame_free(&nframe);
        nframe = nullptr;
    }
}

/*
 * 释放对象(数据包)
 * npacket : 数据包
 */
void TFFmpegThread::freeMember(AVPacket *npacket)
{
    av_packet_unref(npacket);
}

/*
 * 运行
 */
void TFFmpegThread::run()
{
    qDebug() << "==========running===============";
    m_startTime = av_gettime();
    bool nret = init();
    //播放音频
    m_ffmpegAudioPlayThread->setStartTime(m_startTime);
    m_ffmpegAudioPlayThread->setTimeBase(m_ffcustomPlayer.format_ctx->streams[m_ffcustomPlayer.audio_stream_idx]->time_base);
    m_ffmpegAudioPlayThread->startPlay();
    //播放视频
    m_ffmpegVideoPlayThread->setStartTime(m_startTime);
    m_ffmpegVideoPlayThread->setTimeBase(m_ffcustomPlayer.format_ctx->streams[m_ffcustomPlayer.video_stream_idx]->time_base);
    m_ffmpegVideoPlayThread->startPlay();
    if(nret) {
        while(!m_stopped) {
            QElapsedTimer ncodectime;
            ncodectime.start();
            int nframefinish = av_read_frame(m_ffcustomPlayer.format_ctx,m_packet);
            if(nframefinish >= 0) {
                int nindex = m_packet->stream_index;
                //视频包
                if(nindex == m_ffcustomPlayer.video_stream_idx) {
                    m_ffcustomPlayer.videoAppend(av_packet_clone(m_packet));
                }
                //音频包
                else if(nindex == m_ffcustomPlayer.audio_stream_idx) {
                    m_ffcustomPlayer.audioAppend(av_packet_clone(m_packet));
                }
            }
            else {
                qDebug() << "read frame fail" << TFFmpegSupporter::getError(nframefinish) << QTime::currentTime();
                break;
            }
            if(m_ffcustomPlayer.audiopktList.size() >= 500) {
                QThread::msleep(10);
            }
            freeMember(m_packet);
        }
    }
    while(!m_stopped) {
        QThread::msleep(1000);
    }
    freeMember();
    //    emit sigMediaFinish();
    qDebug() << "==========exit===============";
}
